﻿/***************************************************
Copyright (C) 2012 Alexander Forselius

Permission is hereby granted, free of charge, to any person obtaining a copy of this 
software and associated documentation files (the "Software"), to deal in the Software
 without restriction, including without limitation the rights to use, copy, modify, merge,
 publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 
 to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or 
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Threading;
using Newtonsoft.Json;

namespace Bungalow
{
    public partial class Bungalow : Form
    {
        
        private Dictionary<String, IPlayEngine> mediaEngines = new Dictionary<string, IPlayEngine>();
        public Bungalow()
        {
            InitializeComponent();
            tmrTopView = new System.Windows.Forms.Timer();
            tmrTopView.Tick += new EventHandler(tmrTopView_Tick);
            tmrBottomView = new System.Windows.Forms.Timer();
            tmrBottomView.Tick += new EventHandler(tmrBottomView_Tick);
           
        }

        void tmrBottomView_Tick(object sender, EventArgs e)
        {
            if (footerView.IsBrowserInitialized)
            {
                footerView.Load(bottomSrc);
                tmrBottomView.Stop();
            }
        }
        String headerSrc ="";
        String bottomSrc = "";
        void tmrTopView_Tick(object sender, EventArgs e)
        {
            if (headerView.IsBrowserInitialized)
            {
                headerView.Load(headerSrc);
                tmrTopView.Stop();
            }

            
        }   
        private SPListItem AddItem(String text, Uri uri)
        {
            return listView.AddItem(text, uri);
        }
        public Dictionary<String, JObject> Manifests = new Dictionary<string, JObject>();
        public String ApplicationBase {
            get {
                return  Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\";
            }
        }
        private void ListProjects()
        {
            String bungalow_app_dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\";
            if (Directory.Exists(bungalow_app_dir))
            {
                DirectoryInfo dir = new DirectoryInfo(bungalow_app_dir);
                foreach (DirectoryInfo directory in dir.GetDirectories())
                {
                    SPListItem item = listView.AddItem(directory.Name, new Uri("bungalow:app:" + directory.Name.ToLower()), Properties.Resources.folder);
              /*      string manifest_path = directory.FullName + "\\manifest.json";
                    if (File.Exists(manifest_path))
                    {
                        using (StreamReader sr = new StreamReader(manifest_path))
                        {
                            JObject manifest = JObject.Parse(sr.ReadToEnd());
                            Manifests.Add(directory.Name.ToLower(), manifest);
                        }
                    }*/
                }
            }
        }
        private void ListDirectory(String project, DirectoryInfo Dir, SPListItem item, ref String path)
        {
         //   SPListItem add_file = item.AddItem("Add File", new Uri("bungalow:app:" + project + ":" + path + "#AddFile"), Resources.ic_add);
           // SPListItem add_folder = item.AddItem("Add Folder", new Uri("bungalow:app:" + project + ":" + path + "#AddFile"), Resources.ic_add);

            // Show all directories
            foreach (DirectoryInfo directory in Dir.GetDirectories())
            {
                String sub_path = path + ":" + directory.Name;
                SPListItem dir = item.AddItem(directory.Name, new Uri("bungalow:app:" + project + ":" + sub_path + ":"), Properties.Resources.folder);
                ListDirectory(project, directory, dir, ref sub_path);
            }
            // Show all directories
            foreach (FileInfo file in Dir.GetFiles())
            {
                String sub_path = path + ":" + file.Name;
                SPListItem dir = item.AddItem(file.Name, new Uri("bungalow:app:" + project + ":" + sub_path + ":"), Properties.Resources.folder);
                switch (file.Extension)
                {
                    case ".js":

                //        dir.Icon = Properties.Resources.script;
                        break;
                    case ".json":

                  //      dir.Icon = Resources.ic_settings;
                        break;
                    default:
                    //    dir.Icon = Properties.Resources.ic_doc_spotify;
                        break;
                }

            }

        }
        String Locale = "en";
        public Dictionary<String, Control> Windows { get; set; }

        /// <summary>
        /// Loads a project into the tree view
        /// </summary>
        /// <param name="Dir"></param>
        private void LoadProject(DirectoryInfo Dir)
        {
            // Create new listItem
            SPListItem d = AddItem(Dir.Name, new Uri("bungalow:app:" + Dir.Name + ":overview"));
            d.Icon = Properties.Resources.spotifyapp;
            String path = "";
            ListDirectory(Dir.Name, Dir, d, ref path);
           





        }
        private void AssignManifest(string app_name, Uri uri, IApp app)
        {

            String bungalow_app_dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\";
            
            if (!Manifests.ContainsKey(app_name))
            {
                string manifest_path = bungalow_app_dir + app_name  + "\\manifest.json";
                if (File.Exists(manifest_path))
                {
                    using (StreamReader sr = new StreamReader(manifest_path))
                    {
                        JObject _manifest = JObject.Parse(sr.ReadToEnd());
                        Manifests.Add(app_name.ToLower(), _manifest);
                    }
                }
            }
            if(this.tabBar != null)
            this.tabBar.Views.Clear(); // clear tab bar
            // Show tabbar if the manifest is loaded
            JObject manifest = null;
            if(!Manifests.TryGetValue(app_name, out manifest)) {
                MessageBox.Show("The app does not exist");
                return ;
            }
            JToken defaultTabs = null;
            JToken Subscriptions = null;
           
            if (manifest.TryGetValue("DefaultTabs", out defaultTabs))
            {
                appHeader.Mode = AppNotify.HeaderMode.Add;
                JArray tabs = ((JArray)defaultTabs);
                for (var i = 0; i < tabs.Count; i++)
                {
                    var tab = tabs[i];
                    this.tabBar.Views.Add((String)tab["title"][this.Locale], new SPTab() { Uri = new Uri("bungalow:app:" + app_name + ":" + (String)tab["arguments"]), Title = (String)tab["title"][this.Locale] });
                }
                this.tabBar.Show();
                // Activate the current uri
                foreach (SPTab tab in this.tabBar.Views.Values)
                {
                    if (tab.Uri.ToString() == uri.ToString())
                    {
                        this.tabBar.ActiveTab = tab;
                    }
                }
                this.tabBar.Refresh();

            }
            JToken Extensions = null;
            if (manifest.TryGetValue("Extensions", out Extensions))
            {

            }
                JToken Icons = null;
                if (manifest.TryGetValue("AppIcon", out Icons))
                {
                    try
                    {



                        String path = bungalow_app_dir + app_name;
                        Image src = Bitmap.FromFile(path + "\\" + (String)Icons["36x18"]);
                        Bitmap target = new Bitmap(18, 18);
                        Bitmap targetSelected = new Bitmap(18, 18);
                        using (Graphics g = Graphics.FromImage(target))
                        {
                            g.DrawImage(src, new Rectangle(0, 0, 18, 18), new Rectangle(0, 0, 18, 18), GraphicsUnit.Pixel);
                        }
                        using (Graphics g = Graphics.FromImage(targetSelected))
                        {
                            g.DrawImage(src, new Rectangle(0, 0, 18, 18), new Rectangle(0, 18, 18, 18), GraphicsUnit.Pixel);
                        }

                        app.AppIcon.Add("36x18", target);
                        app.AppIcon.Add("36x18:sel", targetSelected);
                        app.AppIcon.Add("24x24", Bitmap.FromFile(path + "\\" + (String)Icons["36x18"]));
                        app.AppIcon.Add("32x32", Bitmap.FromFile(path + "\\" + (String)Icons["32x32"]));
                        app.AppIcon.Add("64x64", Bitmap.FromFile(path + "\\" + (String)Icons["64x64"]));
                        app.AppIcon.Add("128x128", Bitmap.FromFile(path + "\\" + (String)Icons["128x128"]));
                        app.AppIcon.Add("2568x256", Bitmap.FromFile(path + "\\" + (String)Icons["256x256"]));
                    }
                    catch (Exception e)
                    {

                    }
                }
                if (manifest.TryGetValue("AppSubscriptions", out Subscriptions))
                {
                    JArray subscriptions = (JArray)Subscriptions;
                    for (var i = 0; i < subscriptions.Count; i++)
                    {
                        String parameters = (String)subscriptions[i]["arguments"];
                        String name = (String)subscriptions[i]["title"][Locale];
                        if (uri.ToString().StartsWith("bungalow:app:" + app_name + ":" + parameters))
                        {
                            if (!listView.HasURI(uri))
                            {
                                appHeader.Mode = AppNotify.HeaderMode.Subscribe;
                                appHeader.Uri = uri;
                                appHeader.App = app;
                                appHeader.Show();
                            }
                        }
                    }
                }
            }
           
        
        public Dictionary<String, IApp> Apps { get; set; }
        
        public void Navigate(Uri uri)
        {
            if (uri.ToString().StartsWith("urn:"))
            {
                try
                {
                    String[] parts = uri.ToString().Split('#')[0].Split(':');
                    String type = parts[1];
                    String value = parts[2];
                    String action = uri.ToString().Split('#')[1];
                    String handler = Program.Handlers[type];
                    String newUri = handler.Replace("{$action}", action).Replace("{$value}", value).Replace("{$type}", type);
                    Navigate(new Uri(newUri));
                    return;
                }
                catch (Exception e)
                {
                    return;
                }
            }
            if (uri.ToString().StartsWith("spotify:"))
            {
                uri = new Uri(uri.ToString().Replace("spotify:", "bungalow:"));
            }
           
                String[] path = uri.ToString().Split(':');
                switch (path[1])
                {
                    case "app":
                        // If the app is already on the view stack, activate it and
                        // send it  a new uri
                        var app_name = path[2];
                        String header_src = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\" + app_name + "\\header.html";
                        String footer_src = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\" + app_name + "\\footer.html";
            
                        if (Apps.ContainsKey(app_name))
                        {
                          
                            IApp app = Apps[app_name];
                            if (!listView.HasURI(new Uri("bungalow:app:" + path[2])))
                            {
                                appHeader.Uri = uri;
                                appHeader.App = app;
                                appHeader.Show();
                            }
                            else
                            {
                                appHeader.Hide();
                            }
                            AssignManifest(app_name, uri, app);
                            

                            app.Navigate(uri);

                            app.BringToFront();
                      
                        }
                        else
                        {
                            
                            IApp app = new WebApp(app_name, this);
                            AssignManifest(app_name, uri, app);
                            
                            app.LoadFile(Program.BUNGALOW_PROTOCOL + app_name + "/index.html");
                            // Initiate a new app
                            Apps.Add(app_name, app);
                            if (!listView.HasURI(new Uri("bungalow:app:" + path[2])))
                            {
                                appHeader.Uri = uri;
                                appHeader.App = app;
                                appHeader.Show();
                            }
                            else
                            {
                                appHeader.Hide();
                            }
                            this.appStack.Controls.Add((Control)app);
                            ((Control)app).Dock = DockStyle.Fill;
                            ((Control)app).BringToFront();
                            try
                            {
                                app.Navigate(uri);
                            }
                            catch (Exception e)
                            {
                            }
                            if (app.GetType() == typeof(WebApp))
                            {
                                ((WebApp)app).StartDownloadResource += new WebApp.ResourceLoadEventHandler(app_startdownload);
                            }
                        }
                        if (File.Exists(header_src))
                        {
                            if (!headerView.IsBrowserInitialized)
                            {
                                headerSrc = "bl://" + app_name + "/header.html";
                                tmrTopView.Start();

                            }
                            else
                            {
                               

                                headerView.Load("bl://" + app_name + "/header.html");
                            }
                            headerView.Show();
                        }
                        else
                        {
                            headerView.Hide();
                        }


                        if (File.Exists(footer_src))
                        {
                            if (!headerView.IsBrowserInitialized)
                            {
                                bottomSrc = "bl://" + app_name + "/footer.html";
                                tmrBottomView.Start();

                            }
                            else
                            {
                                footerView.Show();
                                
                                footerView.Load("bl://" + app_name + "/footer.html");
                            }
                            footerView.Show();
                        }
                        else
                        {
                            footerView.Hide();
                        }
                        break;
                }
          
        }
        System.Windows.Forms.Timer tmrTopView = new System.Windows.Forms.Timer();
        System.Windows.Forms.Timer tmrBottomView = new System.Windows.Forms.Timer();
        public void app_startdownload(object sender, WebApp.ResourceLoadEventArgs e)
        {
            // Check if the url is authorized by the url
            JObject manifest  = Manifests[e.App.Namespace];
            if (e.Request.Request.Url.StartsWith("spotify:") || e.Request.Request.Url.StartsWith("bungalow:"))
            {
                Navigate(new Uri(e.Request.Request.Url));
                e.Cancel = true;
                return;
            }
            bool permitted = false;
            try
            {
                foreach (JToken str in manifest["RequiredPermissions"])
                {
                    if (str.GetType() == typeof(String))
                    {
                        try
                        {

                            if (e.Request.Request.Url.StartsWith((String)str))
                            {
                                permitted = true;
                            }
                        }
                        catch (Exception ex)
                        {
                        }
                    }
                }
            }
            catch (Exception ex)
            {
            }
           if (e.Request.Request.Url.StartsWith("bl://"))
           {
               permitted = true;
           }
           if (!e.Request.Request.Url.Contains("://"))
           {
               permitted = true;
           }
           e.Cancel = !permitted;
            
        }
        void CreateList()
        {
            listView.Items.Clear();
            listView.AddItem("#Start", new Uri("bungalow:start"));
            listView.AddItem("Start", new Uri("bungalow:start"));
            listView.AddItem("#", new Uri("bungalow:start"));
            listView.AddItem("#Apps", new Uri("bungalow:start"));
        }
        SPListView listView;
        Panel appStack;
        SPTabView tabBar;
        AppNotify appHeader;
        CefSharp.WinForms.WebView headerView;
        CefSharp.WinForms.WebView footerView;
        public bool Play(Uri uri)
        {
            String[] path = uri.ToString().Split();
            if (path.Length > 1 && (path[1] == "track"))
            {
                if (mediaEngines.ContainsKey(path[0]))
                {
                    // Stop all playing activities
                   foreach (IPlayEngine engine in mediaEngines.Values)
                   {
                        engine.Stop();
                   }
                   IPlayEngine _engine = mediaEngines[path[0]];
                    _engine.Play(uri);
                    return true;
                }
            }
            return false;
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            if (!Directory.Exists(Properties.Settings.Default.ResDir))
            {
                MessageBox.Show("You must specify the app dir");
                frmOptions options = new frmOptions();
                options.ShowDialog();
            }
            Apps = new Dictionary<string, IApp>();
            CefSharp.BrowserSettings settings = new CefSharp.BrowserSettings();
            settings.WebSecurityDisabled = true;
            listView = new SPListView();
            CreateList();
            
            listView.ItemSelected += new SPListView.SPListItemMouseEventHandler(listView_ItemSelected);
            appStack = new Panel();
            footerView = new CefSharp.WinForms.WebView("", settings);
            appStack.Controls.Add(footerView);
            footerView.Dock = DockStyle.Bottom;
            footerView.Height = 64;
            footerView.Hide();
            this.panel3.Controls.Add(appStack);
            appStack.Dock = DockStyle.Fill;
            appStack.BackColor = Program.Skin.BackgroundColor;
           
            this.panel3.Controls.Add(listView);
            
            tabBar = new SPTabView();
            appHeader = new AppNotify();
            appHeader.AddApp += new AppNotify.AppActionEventHandler(appHeader_AddApp);
            appHeader.Visible = false;
            appHeader.Subscribe += new AppNotify.AppActionEventHandler(appHeader_Subscribe);
            appStack.Controls.Add(tabBar);
           
            headerView = new CefSharp.WinForms.WebView("", settings);
            appStack.Controls.Add(headerView);
            headerView.Dock = DockStyle.Top;
            headerView.Height = 64;
            headerView.Hide();
            appStack.Controls.Add(appHeader);
            appHeader.Dock = DockStyle.Top;
            tabBar.Height = 26;
            tabBar.Dock = DockStyle.Top;
            tabBar.Hide();
            tabBar.TabChanged += new SPTabView.TabEventHandler(tabBar_TabChanged);
            listView.Width = 220;
            listView.Dock = DockStyle.Left;

            Panel sv = new Panel();
            sv.Dock = DockStyle.Right;
            sv.Width = 220;
            this.panel3.Controls.Add(sv);
            sv.BackColor = Program.Skin.BackgroundColor;
            LoadList();
            Navigate(new Uri("bungalow:app:test"));
            
       //     ListProjects();
            
        }

        void appHeader_Subscribe(object sender, AppNotify.AppActionEventArgs e)
        {
            String appUri = "bungalow:app:" + e.Uri.ToString().Split(':')[2];
            SPListItem item = listView.GetAppByUri(new Uri(appUri));
            if (item != null)
            {
                // invoke subscribe
              String title = (String)e.App.ExecuteScript("__appStateSubscribed('" + e.Uri + "')");
                SPListItem subscription = item.AddItem(title, e.Uri, e.App.AppIcon["36x18"]);
                appHeader.Hide();
            }
            SaveList();
        }
        public String AppFolder {
            get{
              return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\bungalow";
            }

        }
        void PrepareAppData() {
            if (!Directory.Exists(AppFolder))
            {
                Directory.CreateDirectory(AppFolder);
            }
        }
        void SaveList()
        {
            JObject c = new JObject();
            JArray apps = new JArray();
            int start = listView.GetIndexByName("#Apps");
            for (var i = start + 1; i < listView.Items.Count; i++)
            {
                var item = listView.Items[i];
                JObject App = new JObject();
                App.Add("title", (new JValue(item.Text)));
                App.Add("uri", new JValue(item.Uri.ToString()));
                JArray subscriptions = new JArray();
                // Add all subscriptionss
                foreach(SPListItem subItem in item.Children) {
                    JObject subscription = new JObject();
                    subscription.Add("title", new JValue(subItem.Text));
                    
                    subscription.Add("icon", ApplicationBase + "\\" +item.Uri.ToString().Split(':')[2] + "\\" + (Manifests[subItem.Uri.ToString().Split(':')[2]]["AppSubscriptions"][0]["icon"]["36x18"]));
                    subscription.Add("arguments", new JValue(subItem.Uri.ToString().Replace("spotify:app:" + item.Uri.ToString().Split(':')[2], "").Replace("bungalow:app:" + item.Uri.ToString().Split(':')[2], "")));
                    subscriptions.Add(subscription);
  
                }
                App.Add("subscriptions", subscriptions);
                apps.Add(App);
            }
            c.Add("apps", apps);
            PrepareAppData();
            String f =  JsonConvert.SerializeObject(c, Formatting.Indented);
            using (StreamWriter SR = new StreamWriter(AppFolder + "\\apps.json"))
            {
                SR.Write(f);
                SR.Close();
            }
        }
        void LoadList()
        {
            String bungalow_app_dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Bungalow\\";
            CreateList();
            PrepareAppData();
            String fileName = AppFolder + "\\apps.json";
            if (File.Exists(fileName))
            {
                using (StreamReader sr = new StreamReader(fileName))
                {
                    JObject c = JObject.Parse(sr.ReadToEnd());
                    JToken apps = null;
                  
                    if (c.TryGetValue("apps", out apps))
                    {
                        JArray _apps = (JArray)apps;
                        foreach (JObject app in _apps)
                        {
                            String icon = (String)app["Icon"];
                            
                            String title = (String)app["title"];
                            String uri = (String)app["uri"];
                            AssignManifest(title, new Uri(uri), null);
                            var listItem = listView.AddItem(title, new Uri(uri));
                            try
                            {
                                JToken iconDir = Manifests[uri.Split(':')[2]]["AppIcon"];
                                listItem.Icon = Bitmap.FromFile(bungalow_app_dir +  uri.Split(':')[2]+ "\\" + iconDir["36x18"]);
                            }
                            catch (Exception e) { }
                            JToken subs = null;
                            if (app.TryGetValue("subscriptions", out subs))
                            {
                                JArray Subscriptions = (JArray)subs;
                                foreach (JObject subscription in Subscriptions)
                                {
                                    String _title = (String)subscription["title"];
                                    String _arguments = (String)subscription["arguments"];
                                    String _icon = (String)subscription["icon"];
                                    listItem.AddItem(_title, new Uri("bungalow:app:" + uri.Split(':')[2] + ":" + _arguments), (Image)Bitmap.FromFile(_icon));
                                   
                                }
                            }
                        }
                        
                    }
                    sr.Close();
                }
            }
        }
        void appHeader_AddApp(object sender, AppNotify.AppActionEventArgs e)
        {
            appHeader.Hide();
            try
            {
                var item = listView.AddItem(appHeader.App.Title, appHeader.Uri, appHeader.App.AppIcon["36x18"]);
            }
            catch (Exception ex)
            {
                var item = listView.AddItem(appHeader.App.Title, appHeader.Uri);
            }
            listView.Refresh();
            SaveList();
        }

        void tabBar_TabChanged(object sender, SPTabView.TabEventArgs e)
        {
            this.navigate(e.Tab.Uri.ToString());
        }

        void listView_ItemSelected(object Sender, SPListView.SPListItemEventArgs e)
        {
            Navigate(e.Item.Uri);
        }
        private void navigate(String c)
        {
            
            Navigate(new Uri(c));
        }
        private void panel3_Paint(object sender, PaintEventArgs e)
        {

        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void panel3_Paint_1(object sender, PaintEventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            String query = textBox1.Text;
            if (!query.StartsWith("bungalow:"))
            {
                query = "bungalow:app:search:" + query;
            }
            this.navigate(query);
            
           
        }

        private void panel3_Paint_2(object sender, PaintEventArgs e)
        {

        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmOptions options = new frmOptions();
            options.ShowDialog();
        }
    }
}
